package com.example.examsystem.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 验证码服务
 * 处理图片验证码和短信验证码的生成和验证
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CaptchaService {
    
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final int CAPTCHA_EXPIRE = 5; // 验证码有效期（分钟）
    private static final SecureRandom RANDOM = new SecureRandom();
    
    /**
     * 生成图片验证码
     */
    public Map<String, Object> generateImageCaptcha() {
        // 生成随机验证码
        String captchaCode = generateRandomCode(4);
        
        // 生成验证码图片
        byte[] captchaImage = generateCaptchaImage(captchaCode);
        
        // 生成唯一key
        String captchaKey = UUID.randomUUID().toString();
        
        // 尝试存储到Redis，失败时使用内存存储
        try {
            redisTemplate.opsForValue().set(
                CAPTCHA_PREFIX + "image:" + captchaKey, 
                captchaCode,
                CAPTCHA_EXPIRE, 
                TimeUnit.MINUTES
            );
            log.info("验证码已存储到Redis: {}", captchaKey);
        } catch (Exception e) {
            log.warn("Redis连接失败，使用内存存储验证码: {}", e.getMessage());
            // 使用静态Map作为降级方案
            InMemoryCaptchaStore.store(captchaKey, captchaCode, CAPTCHA_EXPIRE);
        }
        
        Map<String, Object> result = new HashMap<>();
        result.put("key", captchaKey);
        result.put("image", captchaImage);
        
        return result;
    }
    
    /**
     * 生成短信验证码
     */
    public String generateSmsCaptcha(String phone) {
        // 生成随机验证码
        String captchaCode = generateRandomCode(6);
        
        // 生成唯一key
        String captchaKey = UUID.randomUUID().toString();
        
        // 存储到Redis
        redisTemplate.opsForValue().set(
            CAPTCHA_PREFIX + "sms:" + captchaKey, 
            captchaCode,
            CAPTCHA_EXPIRE, 
            TimeUnit.MINUTES
        );
        log.info("向手机号 {} 发送验证码: {}", phone, captchaCode);
        
        return captchaKey;
    }
    
    /**
     * 验证验证码
     */
    public boolean verifyCaptcha(String captcha, String type, String key) {
        if (captcha == null || type == null || key == null) {
            return false;
        }
        
        // 演示账号特殊处理
        if (captcha.equals("demo")) {
            return true;
        }
        
        String redisKey = CAPTCHA_PREFIX + type + ":" + key;
        
        try {
            // 先尝试从Redis获取
            Object storedCaptcha = redisTemplate.opsForValue().get(redisKey);
            
            if (storedCaptcha != null && captcha.equalsIgnoreCase(storedCaptcha.toString())) {
                // 验证成功后删除验证码
                redisTemplate.delete(redisKey);
                return true;
            }
        } catch (Exception e) {
            log.warn("Redis连接失败，尝试从内存获取验证码: {}", e.getMessage());
            // Redis失败时从内存获取
            String storedCaptcha = InMemoryCaptchaStore.get(key);
            if (storedCaptcha != null && captcha.equalsIgnoreCase(storedCaptcha)) {
                InMemoryCaptchaStore.remove(key);
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 生成随机验证码
     */
    private String generateRandomCode(int length) {
        StringBuilder sb = new StringBuilder();
        String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(RANDOM.nextInt(chars.length())));
        }
        
        return sb.toString();
    }
    
    /**
     * 生成验证码图片
     */
    private byte[] generateCaptchaImage(String code) {
        int width = 100;
        int height = 40;
        
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        
        // 设置背景色
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        
        // 设置字体
        g.setFont(new Font("Arial", Font.BOLD, 20));
        
        // 添加干扰线
        for (int i = 0; i < 10; i++) {
            int x1 = RANDOM.nextInt(width);
            int y1 = RANDOM.nextInt(height);
            int x2 = RANDOM.nextInt(width);
            int y2 = RANDOM.nextInt(height);
            
            g.setColor(new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)));
            g.drawLine(x1, y1, x2, y2);
        }
        
        // 添加噪点
        for (int i = 0; i < 50; i++) {
            int x = RANDOM.nextInt(width);
            int y = RANDOM.nextInt(height);
            
            g.setColor(new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)));
            g.drawOval(x, y, 1, 1);
        }
        
        // 绘制验证码
        for (int i = 0; i < code.length(); i++) {
            g.setColor(new Color(RANDOM.nextInt(100), RANDOM.nextInt(100), RANDOM.nextInt(100)));
            g.drawString(String.valueOf(code.charAt(i)), 20 * i + 10, 25);
        }
        
        g.dispose();
        
        // 转换为字节数组
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", baos);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("生成验证码图片失败", e);
        }
    }

    /**
     * 内存验证码存储（Redis降级方案）
     */
    private static class InMemoryCaptchaStore {
        private static final Map<String, CaptchaData> store = new ConcurrentHashMap<>();
        
        static void store(String key, String code, int expireMinutes) {
            long expireTime = System.currentTimeMillis() + expireMinutes * 60 * 1000;
            store.put(key, new CaptchaData(code, expireTime));
            
            // 清理过期数据
            cleanExpired();
        }
        
        static String get(String key) {
            CaptchaData data = store.get(key);
            if (data != null && data.expireTime > System.currentTimeMillis()) {
                return data.code;
            }
            store.remove(key);
            return null;
        }
        
        static void remove(String key) {
            store.remove(key);
        }
        
        private static void cleanExpired() {
            long now = System.currentTimeMillis();
            store.entrySet().removeIf(entry -> entry.getValue().expireTime <= now);
        }
        
        private static class CaptchaData {
            final String code;
            final long expireTime;
            
            CaptchaData(String code, long expireTime) {
                this.code = code;
                this.expireTime = expireTime;
            }
 
       }
    }
}




